﻿#pragma once

#include <cstddef>
#include <cstdint>
#include <vector>

class ZRingBuffer {
 public:
  ZRingBuffer() = default;

  // 拷贝构造函数
  ZRingBuffer(const ZRingBuffer &other);

  // 移动构造函数
  ZRingBuffer(ZRingBuffer &&other) noexcept;

  /**
   * @param size buffer size
   */
  ZRingBuffer(size_t size);

  /**
   * 使用外部的存储容器,用于特殊场景,例如一个达到30MB的buffer,只能用外部static存储
   * @param data 指针的起始地址
   * @param size buffer长度
   */
  ZRingBuffer(void *data, size_t size);

  ~ZRingBuffer();

  /**
   * @return 当前buffer的大小
   */
  size_t size() const;

  /**
   * @return 当前buffer的容量,即最大可以存储的字节数
   */
  size_t capacity() const;

  /**
   * @return buffer的数据起始指针,因为是环形的,所以可能不是线性的
   */
  uint8_t *data();

  /**
   * @return 线性的数据,如果数据是环形的,则会重新分配一块内存,并复制数据
   */
  ZRingBuffer linearData();

  /**
   * 清空当前的buffer,让索引回到初始位置
   */
  void clean();

  /**
   * 调整buffer的大小,会重新开辟一块内存,丢掉以前的数据
   * @param size 新的buffer大小
   */
  void resize(size_t size);

  /**
   * 返回索引位置的值
   * @param pos 索引位置
   */
  uint8_t at(size_t pos);

  /**
   * 重载[]操作符
   * @param pos 索引位置
   * @return 位置的值
   */
  uint8_t &operator[](size_t pos);

  /**
   * 在buffer的前面添加一个值,会让读取的索引向前移动
   * @param value 值
   */
  void push_front(uint8_t value);

  /**
   * 在buffer的前面添加一段数据,增加的顺序需要保持一致,会让读取的索引向前移动
   * @param data 数据指针
   * @param size 数据的大小
   */
  void push_front(void *data, size_t size);

  /**
   * 在buffer的后面添加一个值,会让写入的索引向后移动
   * @param value 值
   */
  void push_back(uint8_t value);

  /**
   * 在buffer的后面添加一段数据,增加的顺序需要保持一致,会让写入的索引向后移动
   * @param data 数据指针
   * @param size 数据的大小
   */
  void push_back(void *data, size_t size);

  /**
   * 从buffer的前面删除值,会让读取的索引向后移动
   * @param size 元素的个数
   */
  void pop_front(size_t size = 1);

  /**
   * 从buffer的后面删除值,会让写入的索引向前移动
   * @param size 元素的个数
   */
  void pop_back(size_t size = 1);

  /**
   * 从buffer的前面复制一段数据,生成一个新的ZRingBuffer对象
   * @param size 元素的个数
   * @return 左侧指定元素的新buffer
   */
  ZRingBuffer left(size_t size);

  /**
   * 从buffer的前面复制一段数据到指定的内存地址
   * @param size 元素的个数
   */
  void left(size_t size, void *data);

  /**
   * 从buffer的后面复制一段数据,生成一个新的ZRingBuffer对象
   * @param size 元素的个数
   * @return 右侧指定元素的新buffer
   */
  ZRingBuffer right(size_t size);

  /**
   * 从buffer的指定位置开始复制指定数量的数据,生成一个新的ZRingBuffer对象
   * @param pos 起始的索引
   * @param len 长度,如果为-1,则表示到buffer的末尾
   * @return 从指定位置开始的buffer,如果pos大于buffer的长度,则返回空的ZRingBuffer
   */
  ZRingBuffer mid(int pos, int len = -1);

  /**
   * 查找目标值在buffer中的索引位置,如果没有找到,则返回-1
   * @param value 目标值
   * @param startPos 起始搜索位置
   * @return 目标值的索引位置,如果没有找到,则返回-1
   */
  int indexOf(uint8_t value, int startPos = 0);

  /**
   * 查找目标数据在buffer中的索引位置,如果没有找到,则返回-1
   * @param data 目标数据的指针
   * @param size 目标数据的大小
   * @param startPos 起始搜索位置
   * @return 目标数据的索引位置,如果没有找到,则返回-1
   */
  int indexOf(void *data, size_t size, int startPos = 0);

  /**
   * 打印当前buffer的状态,用于调试
   */
  void print();

 private:
  std::vector<uint8_t> container_;
  uint8_t *buffer_ = nullptr;
  size_t capacity_ = 0;
  size_t head_ = 0;
  size_t tail_ = 0;
  size_t count_ = 0;
  bool externalBuffer_ = false;
};